//MARK:- 面向协议编程
import CoreGraphics

let twoPi = CGFloat.pi * 2

protocol Renderer {
    
    func move(to p: CGPoint)
    
    func line(to p: CGPoint)
    
    func arc(at center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat)
    
}


struct TestRenderer : Renderer{
    func move(to p: CGPoint) {
        print("move(to: CGPoint(\(p.x), \(p.y)))")
    }
    
    func line(to p: CGPoint) { print("line(to: CGPoint(\(p.x), \(p.y)))") }
    
    func arc(at center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat) {
        print("arc(at: \(center), radius: \(radius)," + " startAngle: \(startAngle), endAngle: \(endAngle))")
    }
}

protocol Drawable {
    func draw(into renderer: Renderer)
}

struct Polygon: Drawable {
    func draw(into renderer: Renderer) {
        renderer.move(to: corners.last!)
        for p in corners {
            renderer.line(to: p)
        }
    }
    
    var corners: [CGPoint] = []
}

struct Circle : Drawable {
    func draw(into renderer: Renderer) {
        renderer.arc(at: center, radius: radius, startAngle: 0.0, endAngle: twoPi)
    }
    var center: CGPoint
    var radius: CGFloat
}


struct Diagram: Drawable {
    
    var elements: [Drawable] = []
    
    func draw(into renderer: Renderer) {
        for f in  elements{
            f.draw(into: renderer)
        }
    }
    
    mutating func add(other: Drawable) {
        elements.append(other)
    }
    
}


extension CGContext: Renderer{
    func line(to position: CGPoint) {
            addLine(to: position)
    }
    
    func arc(at center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat) {
        let arc = CGMutablePath()
        arc.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        addPath(arc)
        
    }
}


var circle = Circle(center: CGPoint(x: 187.5, y: 333.5), radius: 93.75)

var triangle = Polygon(corners: [
    CGPoint(x: 187.5, y: 427.25),
    CGPoint(x: 268.69, y: 286.625),
    CGPoint(x: 106.31, y: 286.625)])

var diagram = Diagram(elements: [circle,triangle])

struct ScaledRenderer: Renderer{
    
    let base: Renderer
    let scale: CGFloat
    
    func move(to p: CGPoint) {
          base.move(to: CGPoint(x: p.x * scale, y: p.y * scale))
    }
    
    func line(to p: CGPoint) {
            base.line(to: CGPoint(x: p.x * scale, y: p.y * scale))
    }
    
    func arc(at center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat) {
        let scaledCenter = CGPoint(x: center.x * scale, y: center.y * scale)
        base.arc(at: scaledCenter, radius: radius * scale, startAngle: startAngle, endAngle: endAngle)
        
    }
    
}


struct Scaled<Base: Drawable>: Drawable{
    
    var scale: CGFloat
    var subject: Base
    
    func draw(into renderer: Renderer) {
        subject.draw(into: ScaledRenderer(base: renderer, scale: scale))
    }
    
}


diagram.elements.append(Scaled(scale: 0.3, subject: diagram))

diagram.draw(into: TestRenderer())

//showCoreGraphicsDiagram(title: "Diagram") { diagram.draw(into: $0) }

